using System;
using System.Collections.Generic;
using System.Text;
using VisualizationPackage.FileLoader;
using System.Collections.ObjectModel;
using System.Drawing;
using Visualization;
//using NUnit.Framework;

namespace VisualizationPackage.Contours
{
    public class Contour
    {
        private ColorMap _distributions = new ColorMap();
        private int _selectedDistribution;

        public int SelectedDistribution
        {
            get { return _selectedDistribution; }
            set { _selectedDistribution = value; }
        }
	    public ColorMap Distributions
	    {
		    get { return _distributions;}
		    set { _distributions = value;}
	    }

        public Contour(int Distribution)
        {
            SelectedDistribution = Distribution;
        }
        public Contour(int Distribution, ColorMap colorMap)
        {
            _distributions = colorMap;
            SelectedDistribution = Distribution;
        }
        private static double LinearInterpolation(double u, double v, Visualization.Zone zone, Face face, int Distribution)
        {
            return LinearInterpolation(u, v, zone.Vertices[face.Vertices[0]].Data[Distribution],
                zone.Vertices[face.Vertices[1]].Data[Distribution],
                zone.Vertices[(face.Vertices.Length > 3) ? 2 : 1].Data[Distribution],
                zone.Vertices[(face.Vertices.Length > 3) ? 3 : 2].Data[Distribution]);
        }
        private static double LinearInterpolationX(double u, double v, Visualization.Zone zone, Face face)
        {
            return LinearInterpolation(u, v, zone.Vertices[face.Vertices[0]].Position.x,
                zone.Vertices[face.Vertices[1]].Position.x,
                zone.Vertices[(face.Vertices.Length > 3) ? 2 : 1].Position.x,
                zone.Vertices[(face.Vertices.Length > 3) ? 3 : 2].Position.x);
        }
        private static double LinearInterpolationY(double u, double v, Visualization.Zone zone, Face face)
        {
            return LinearInterpolation(u, v, zone.Vertices[face.Vertices[0]].Position.y,
                zone.Vertices[face.Vertices[1]].Position.y,
                zone.Vertices[(face.Vertices.Length > 3) ? 2 : 1].Position.y,
                zone.Vertices[(face.Vertices.Length > 3) ? 3 : 2].Position.y);
        }
        private static double LinearInterpolationZ(double u, double v, Visualization.Zone zone, Face face)
        {
            return LinearInterpolation(u, v, zone.Vertices[face.Vertices[0]].Position.z,
                zone.Vertices[face.Vertices[1]].Position.z,
                zone.Vertices[(face.Vertices.Length > 3) ? 2 : 1].Position.z,
                zone.Vertices[(face.Vertices.Length > 3) ? 3 : 2].Position.z);
        }
        private static double LinearInterpolation(double u, double v, double p1, double p2, double p3, double p4)
        {
            double val = (1 - u) * (1 - v) * p1 + (1 - u) * v * p2 + u * (1 - v) * p3 + u * v * p4;
            return val;
        }
        private List<Polygon> Triangulate(Visualization.Zone zone, Face face)
        {
            List<Polygon> polys = new List<Polygon>();
            
            Visualization.Vertex vCenter = new Visualization.Vertex();
            vCenter.Data = new double[zone.DataCount];
            vCenter.Position.x = LinearInterpolationX(0.5, 0.5, zone, face);
            vCenter.Position.y = LinearInterpolationY(0.5, 0.5, zone, face);
            vCenter.Position.z = LinearInterpolationZ(0.5, 0.5, zone, face);
            vCenter.Data[SelectedDistribution] = LinearInterpolation(0.5, 0.5, zone, face, SelectedDistribution);

            Polygon polygon = new Polygon();
            for (int i = 0; i < face.Vertices.Length; i++)
                polygon.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[face.Vertices[i]]));
            polygon.Vertics.Add(polygon.Vertics[0]);
            for (int i = 0; i < polygon.Vertics.Count - 1; i++)
            {
                Polygon p = new Polygon();
                p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(vCenter));
                p.Vertics.Add(polygon.Vertics[i]);
                p.Vertics.Add(polygon.Vertics[i + 1]);
                polys.Add(p);
            }
            polygon.Vertics.RemoveAt(polygon.Vertics.Count - 1);
            return polys;
        }

        public ReadOnlyCollection<Line> LineContour(Visualization.Zone zone)
        {
            List<Line> res = new List<Line>();
            for (int i = 0; i < zone.FaceCount; i++)
            {
                Polygon p = new Polygon();
                p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[0]]));
                p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[1]]));
                p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[2]]));
                //p.Sort();
                res.AddRange(GetLines(1, p));

                if (zone.Faces[i].Vertices.Length > 3)
                {
                    p = new Polygon();
                    p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[2]]));
                    p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[3]]));
                    p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[0]]));
                    //p.Sort();
                    res.AddRange(GetLines(1, p));
                }
            }
            return res.AsReadOnly();

            //List<Line> res = new List<Line>();
            //for (int i = 0; i < zone.FaceCount; i++)
            //{
            //    List<Polygon> polys = Triangulate(zone, zone.Faces[i]);
            //    foreach (Polygon p in polys)
            //        res.AddRange(GetLines(1, p));
            //}
            //return res.AsReadOnly();
        }

        public List<Line> GetLines(int start, Polygon polygon)
        {
            if (start >= Distributions.Map.Count)// || polygon.Vertics.Count <= 2)
                return new List<Line>();
            Line line = new Line();
            List<Line> lines = new List<Line>();
            Polygon pRest = new Polygon();
            polygon.Vertics.Add(polygon.Vertics[0]);
            for (int i = 0; i < polygon.Vertics.Count - 1; i++)
            {
                line.DrawingColor = Distributions.Map[start].NodeColor;// Color.FromArgb((Distributions.Map[start].NodeColor.ToArgb() + Distributions.Map[start - 1].NodeColor.ToArgb()));
                if (polygon.Vertics[i][GlobalVariables.Data[SelectedDistribution]] == Distributions.Map[start].Value && polygon.Vertics[i + 1][GlobalVariables.Data[SelectedDistribution]] == Distributions.Map[start].Value)
                    if (line.Vertex1 == null)
                    {
                        line.Vertex1 = polygon.Vertics[i];
                        line.Vertex2 = polygon.Vertics[i + 1];
                        lines.Add(line);
                        line = new Line();
                        line.Vertex1 = polygon.Vertics[i + 1];
                    }
                    else
                    {
                        line.Vertex2 = polygon.Vertics[i];
                        lines.Add(line);
                        line = new Line();
                        line.DrawingColor = Distributions.Map[start].NodeColor;
                        line.Vertex1 = polygon.Vertics[i];
                        line.Vertex2 = polygon.Vertics[i + 1];
                        lines.Add(line);
                        line = new Line();
                        line.Vertex1 = polygon.Vertics[i + 1];
                    }
                else if (polygon.Vertics[i][GlobalVariables.Data[SelectedDistribution]] <= Distributions.Map[start].Value
                    && polygon.Vertics[i + 1][GlobalVariables.Data[SelectedDistribution]] >= Distributions.Map[start].Value)
                {
                    VisualizationPackage.FileLoader.Vertex v = VisualizationPackage.FileLoader.Vertex.Interpolate(GlobalVariables.Data[SelectedDistribution], Distributions.Map[start].Value, polygon.Vertics[i], polygon.Vertics[i + 1]);

                    if (line.Vertex1 == null)
                        line.Vertex1 = v;
                    else
                    {
                        line.Vertex2 = v;
                        lines.Add(line);
                        line = new Line();
                        line.Vertex1 = v;
                    }

                    pRest.Add(v);
                    pRest.Add(polygon.Vertics[i + 1]);
                }
                else if (polygon.Vertics[i][GlobalVariables.Data[SelectedDistribution]] >= Distributions.Map[start].Value
                    && polygon.Vertics[i + 1][GlobalVariables.Data[SelectedDistribution]] <= Distributions.Map[start].Value)
                {
                    VisualizationPackage.FileLoader.Vertex v = VisualizationPackage.FileLoader.Vertex.Interpolate(GlobalVariables.Data[SelectedDistribution], Distributions.Map[start].Value, polygon.Vertics[i + 1], polygon.Vertics[i]);

                    if (line.Vertex1 == null)
                        line.Vertex1 = v;
                    else
                    {
                        line.Vertex2 = v;
                        lines.Add(line);
                        line = new Line();
                        line.Vertex1 = v;
                    }

                    pRest.Add(v);
                    pRest.Add(polygon.Vertics[i]);
                }
                else if (polygon.Vertics[i][GlobalVariables.Data[SelectedDistribution]] > Distributions.Map[start].Value
                    && polygon.Vertics[i + 1][GlobalVariables.Data[SelectedDistribution]] > Distributions.Map[start].Value)
                {
                    pRest.Add(polygon.Vertics[i]);
                    pRest.Add(polygon.Vertics[i + 1]);
                }
            }
            polygon.Vertics.RemoveAt(polygon.Vertics.Count - 1);

            lines.AddRange(GetLines(start + 1, polygon));
            return lines;
        }
        public ReadOnlyCollection<Polygon> FloodedContour(Visualization.Zone zone)
        {
            List<Polygon> res = new List<Polygon>();
            for (int i = 0; i < zone.FaceCount; i++)
            {
                Polygon p = new Polygon();
                p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[0]]));
                p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[1]]));
                p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[2]]));
                //p.Sort();
                res.AddRange(GetPolygons(1, p));

                if (zone.Faces[i].Vertices.Length > 3)
                {
                    p = new Polygon();
                    p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[2]]));
                    p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[3]]));
                    p.Vertics.Add(new VisualizationPackage.FileLoader.Vertex(zone.Vertices[zone.Faces[i].Vertices[0]]));
                    //p.Sort();
                    res.AddRange(GetPolygons(1, p));
                }
            }
            return res.AsReadOnly();
        }
        public List<Polygon> GetPolygons(int start, Polygon polygon)
        {
            if (start >= Distributions.Map.Count || polygon.Vertics.Count <= 2)
                return new List<Polygon>();
            Polygon p1 = new Polygon(), p2 = new Polygon();
            List<Polygon> polys;
            
            foreach (FileLoader.Vertex vertex in polygon.Vertics)
            {
                if (vertex[GlobalVariables.Data[SelectedDistribution]] > Distributions.Map[start].Value)
                {
                    p1.Vertics.Clear();
                    break;
                }
                else
                    p1.Vertics.Add(vertex);
            }
            if (p1.Vertics.Count > 0)
            {
                p1.DrawingColor = Distributions.Map[start - 1].NodeColor;
                polys = new List<Polygon>();
                polys.Add(p1);
                return polys;
            }
            
            foreach (FileLoader.Vertex vertex in polygon.Vertics)
            {
                if (vertex[GlobalVariables.Data[SelectedDistribution]] < Distributions.Map[start].Value)
                {
                    p1.Vertics.Clear();
                    break;
                }
                else
                    p1.Add(vertex);
            }
            if (p1.Vertics.Count > 0)
                return GetPolygons(start + 1, p1);
            
            polygon.Vertics.Add(polygon.Vertics[0]);
            for (int i = 0; i < polygon.Vertics.Count - 1; i++)
            {
                p1.DrawingColor = Distributions.Map[start - 1].NodeColor;// Color.FromArgb((Distributions.Map[start].NodeColor.ToArgb() + Distributions.Map[start - 1].NodeColor.ToArgb()) / 2);
                if (polygon.Vertics[i][GlobalVariables.Data[SelectedDistribution]] <= Distributions.Map[start].Value
                    && polygon.Vertics[i + 1][GlobalVariables.Data[SelectedDistribution]] >= Distributions.Map[start].Value)
                {
                    p1.Add(polygon.Vertics[i]);
                    
                    VisualizationPackage.FileLoader.Vertex v = VisualizationPackage.FileLoader.Vertex.Interpolate(GlobalVariables.Data[SelectedDistribution], Distributions.Map[start].Value, polygon.Vertics[i], polygon.Vertics[i + 1]);

                    p1.Add(v);
                    p2.Add(v);
                    p2.Add(polygon.Vertics[i + 1]);
                }
                else if (polygon.Vertics[i][GlobalVariables.Data[SelectedDistribution]] >= Distributions.Map[start].Value
                    && polygon.Vertics[i + 1][GlobalVariables.Data[SelectedDistribution]] <= Distributions.Map[start].Value)
                {
                    p2.Add(polygon.Vertics[i]);
                    VisualizationPackage.FileLoader.Vertex v = VisualizationPackage.FileLoader.Vertex.Interpolate(GlobalVariables.Data[SelectedDistribution], Distributions.Map[start].Value, polygon.Vertics[i + 1], polygon.Vertics[i]);

                    p1.Add(v);
                    p2.Add(v);
                    p1.Add(polygon.Vertics[i + 1]);
                }
                else if (polygon.Vertics[i][GlobalVariables.Data[SelectedDistribution]] > Distributions.Map[start].Value
          && polygon.Vertics[i + 1][GlobalVariables.Data[SelectedDistribution]] > Distributions.Map[start].Value)
                {
                    p2.Add(polygon.Vertics[i]);
                    p2.Add(polygon.Vertics[i + 1]);
                }
                else
                {
                    p1.Add(polygon.Vertics[i]);
                    p1.Add(polygon.Vertics[i + 1]);
                }
            }
            polygon.Vertics.RemoveAt(polygon.Vertics.Count - 1);
            polys = new List<Polygon>();
            //Assert.AreNotEqual(p1.Vertics.Count, 0);
            if (p1.Vertics.Count > 2)
            {
                //p1.Sort();
                polys.Add(p1);
            }
            //if (p2.Vertics.Count > 2)
                //p2.Sort();
            List<Polygon> triangles = new List<Polygon>();
            triangles.Add(p2);
            if (p2.Vertics.Count == 4)
                triangles = Triangulate(p2);
            foreach (Polygon poly in triangles)
                polys.AddRange(GetPolygons(start + 1, poly));
            
            return polys;
        }

        private List<Polygon> Triangulate(Polygon poly)
        {
            List<Polygon> res = new List<Polygon>();

            Polygon p = new Polygon();
            p.Vertics.Add(poly.Vertics[0]);
            p.Vertics.Add(poly.Vertics[1]);
            p.Vertics.Add(poly.Vertics[2]);
            //p.Sort();
            res.Add(p);

            p = new Polygon();
            p.Vertics.Add(poly.Vertics[2]);
            p.Vertics.Add(poly.Vertics[3]);
            p.Vertics.Add(poly.Vertics[0]);
            //p.Sort();
            res.Add(p);

            return res;
        }

		private double _floodedstep;

		public double FloodedStep
		{
			get { return _floodedstep; }
			set { _floodedstep = value; }
		}
	
		public ReadOnlyCollection<Polygon> FloodedContour_1(Visualization.Zone zone)
		{
			FloodedStep = 0.05;
			List<Polygon> polys = new List<Polygon>();
			foreach (Face fc in zone.Faces)
			{
				
				for (double i = FloodedStep; i <= 1.05; i += FloodedStep)
					for (double j = FloodedStep; j <= 1.05; j += FloodedStep)
					{
						Polygon p = new Polygon();
						p.Add(CalculateInternal(i - FloodedStep, j - FloodedStep, fc, zone));
						p.Add(CalculateInternal(i, j - FloodedStep, fc, zone));
						p.Add(CalculateInternal(i, j, fc, zone));
						p.Add(CalculateInternal(i - FloodedStep, j, fc, zone));
						double Val = CalculateParametric(i - FloodedStep / 2.0, j - FloodedStep / 2.0, 
							zone.Vertices[fc.Vertices[1]].Data[SelectedDistribution], 
							zone.Vertices[fc.Vertices[0]].Data[SelectedDistribution], 
							zone.Vertices[fc.Vertices[2]].Data[SelectedDistribution], 
							zone.Vertices[fc.Vertices[ fc.Vertices.Length-1 ]].Data[SelectedDistribution] );
						p.DrawingColor = Distribute(Val);
						polys.Add(p);
					}
			}
			return polys.AsReadOnly();
		}

		private Color Distribute(double value)
		{
			Color prevColor = Distributions.Map[0].NodeColor;
			foreach (ColorMapNode node in Distributions.Map)
			{
				if (node.Value > value)
					return prevColor;
				prevColor = node.NodeColor;
			}
			return prevColor;
		}

		private VisualizationPackage.FileLoader.Vertex CalculateInternal(double i, double j, Face fc, Visualization.Zone zone)
		{
			VisualizationPackage.FileLoader.Vertex vert = new VisualizationPackage.FileLoader.Vertex();
			vert.X = CalculateParametric(i, j, 
				zone.Vertices[fc.Vertices[1]].Position.x, 
				zone.Vertices[fc.Vertices[0]].Position.x, 
				zone.Vertices[fc.Vertices[2]].Position.x,
				zone.Vertices[fc.Vertices[fc.Vertices.Length - 1]].Position.x);

			vert.Y = CalculateParametric(i, j,
				zone.Vertices[fc.Vertices[1]].Position.y,
				zone.Vertices[fc.Vertices[0]].Position.y,
				zone.Vertices[fc.Vertices[2]].Position.y,
				zone.Vertices[fc.Vertices[fc.Vertices.Length - 1]].Position.y);

			vert.Z = CalculateParametric(i, j,
				zone.Vertices[fc.Vertices[1]].Position.z,
				zone.Vertices[fc.Vertices[0]].Position.z,
				zone.Vertices[fc.Vertices[2]].Position.z,
				zone.Vertices[fc.Vertices[fc.Vertices.Length - 1]].Position.z);
			return vert;
		}

		private double CalculateParametric(double i, double j, params double[] values)
		{
			return (1-i) * (1-j) * values[0] +
				   (1-i) * ( j ) * values[1] +
				   ( i ) * (1-j) * values[2] +
				   ( i ) * ( j ) * values[3] ;
		}
	}
}
